步骤 7:添加系统自检

让我们考虑向我们的项目中添加一些代码,这些代码依赖于目标平台可能不具备的功能。 在这个例子中,我们将添加一些代码,这些代码依赖于目标平台是否具有 logexp 函数。 当然,几乎每个平台都有这些函数,但为了本教程的目的,假设它们并不常见。

练习 1 - 评估依赖项可用性

目标

根据可用的系统依赖项更改实现。

有用的资源

要编辑的文件

  • MathFunctions/CMakeLists.txt

  • MathFunctions/mysqrt.cxx

开始入门

起始源代码在 Step7 目录中提供。 在本练习中,完成 TODO 1TODO 5

首先编辑 MathFunctions/CMakeLists.txt。 包含 CheckCXXSourceCompiles 模块。 然后,使用 check_cxx_source_compiles 确定是否可以从 cmath 中获得 logexp。 如果可用,请使用 target_compile_definitions() 来指定 HAVE_LOGHAVE_EXP 作为编译定义。

MathFunctions/mysqrt.cxx 中,包含 cmath。 然后,如果系统具有 logexp,则使用它们来计算平方根。

构建并运行

创建一个名为 Step7_build 的新目录。 运行 cmake 可执行文件或 cmake-gui 来配置项目,然后使用您选择的构建工具构建它,并运行 Tutorial 可执行文件。

这看起来可能像下面这样

mkdir Step7_build
cd Step7_build
cmake ../Step7
cmake --build .

现在哪个函数给出更好的结果,sqrt 还是 mysqrt

解决方案

在本练习中,我们将使用来自 CheckCXXSourceCompiles 模块的函数,因此首先我们必须将其包含在 MathFunctions/CMakeLists.txt 中。

TODO 1:点击显示/隐藏答案
TODO 1:MathFunctions/CMakeLists.txt
  include(CheckCXXSourceCompiles)

然后使用 check_cxx_compiles_source 测试 logexp 的可用性。 此函数允许我们在真正的源代码编译之前尝试使用所需的依赖项编译简单的代码。 生成的变量 HAVE_LOGHAVE_EXP 表示这些依赖项是否可用。

TODO 2:点击显示/隐藏答案
TODO 2:MathFunctions/CMakeLists.txt
  check_cxx_source_compiles("
    #include <cmath>
    int main() {
      std::log(1.0);
      return 0;
    }
  " HAVE_LOG)
  check_cxx_source_compiles("
    #include <cmath>
    int main() {
      std::exp(1.0);
      return 0;
    }
  " HAVE_EXP)

接下来,我们需要将这些 CMake 变量传递给我们的源代码。 这样,我们的源代码就可以知道哪些资源可用。 如果 logexp 都可用,则使用 target_compile_definitions() 来指定 HAVE_LOGHAVE_EXP 作为 PRIVATE 编译定义。

TODO 3:点击显示/隐藏答案
TODO 3:MathFunctions/CMakeLists.txt
  if(HAVE_LOG AND HAVE_EXP)
    target_compile_definitions(SqrtLibrary
                               PRIVATE "HAVE_LOG" "HAVE_EXP"
                               )
  endif()

  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
endif()

由于我们可能会使用 logexp,我们需要修改 mysqrt.cxx 以包含 cmath

TODO 4:点击显示/隐藏答案
TODO 4:MathFunctions/mysqrt.cxx
#include <cmath>

如果系统上 logexp 可用,则在 mysqrt 函数中使用它们来计算平方根。 MathFunctions/mysqrt.cxx 中的 mysqrt 函数将如下所示

TODO 5:点击显示/隐藏答案
TODO 5:MathFunctions/mysqrt.cxx
#if defined(HAVE_LOG) && defined(HAVE_EXP)
  double result = std::exp(std::log(x) * 0.5);
  std::cout << "Computing sqrt of " << x << " to be " << result
            << " using log and exp" << std::endl;
#else
  double result = x;

  // do ten iterations
  for (int i = 0; i < 10; ++i) {
    if (result <= 0) {
      result = 0.1;
    }
    double delta = x - (result * result);
    result = result + 0.5 * delta / result;
    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
  }
#endif